Skip to content

Conversation

@andsel
Copy link
Contributor

@andsel andsel commented Dec 17, 2025

Release notes

[rn:skip]

What does this PR do?

Extract common code from datapoint that can be used by the RetentionWindow to be used to compute a flow metric.
Moves RetentionWindow ExtendedFlowMetric's inner class to be standalone class usable by other metric implementations.

Why is it important/What is the impact to the user?

As a developer of histogram flow metric I want to reuse existing code used to implement ExtendedFlowMetric.

Checklist

  • My code follows the style guidelines of this project
  • I have commented my code, particularly in hard-to-understand areas
  • [ ] I have made corresponding changes to the documentation
  • [ ] I have made corresponding change to the default configuration files (and/or docker env variables)
  • [ ] I have added tests that prove my fix is effective or that my feature works

Author's Checklist

  • [ ]

How to test this PR locally

Related issues

@andsel andsel self-assigned this Dec 17, 2025
@github-actions
Copy link
Contributor

🤖 GitHub comments

Just comment with:

  • run docs-build : Re-trigger the docs validation. (use unformatted text in the comment!)
  • /run exhaustive tests : Run the exhaustive tests Buildkite pipeline.

@mergify
Copy link
Contributor

mergify bot commented Dec 17, 2025

This pull request does not have a backport label. Could you fix it @andsel? 🙏
To fixup this pull request, you need to add the backport labels for the needed
branches, such as:

  • backport-8./d is the label to automatically backport to the 8./d branch. /d is the digit.
  • If no backport is necessary, please add the backport-skip label

@andsel andsel changed the title Fix/extract reusable classes from flow metrics Extract reusable classes from flow metrics Dec 17, 2025
@andsel andsel marked this pull request as ready for review December 17, 2025 14:43
@andsel andsel requested a review from Copilot December 23, 2025 09:04
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extracts reusable classes from the ExtendedFlowMetric implementation to enable future support for histogram flow metrics. The refactoring moves the RetentionWindow inner class to a standalone class and creates a new DatapointCapture base class to generalize capture types beyond just FlowCapture.

Key changes:

  • Introduced DatapointCapture as an abstract base class for point-in-time metric data with timing information
  • Refactored FlowCapture to extend DatapointCapture, moving common nanoTime handling to the base class
  • Extracted RetentionWindow from an inner class to a standalone package-private class that works with DatapointCapture instead of just FlowCapture

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
DatapointCapture.java New abstract base class providing timing information and capture selection logic for all datapoint types
FlowCapture.java Refactored to extend DatapointCapture, removing duplicated nanoTime field and method
RetentionWindow.java Extracted from ExtendedFlowMetric as a standalone class, generalized to work with DatapointCapture instead of FlowCapture
ExtendedFlowMetric.java Removed inner RetentionWindow class and selectNewerCapture utility method, added cast when consuming baseline captures

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 99 to 101
.map((baseline) -> calculateRate(currentCapture, (FlowCapture) baseline))
.orElseGet(OptionalDouble::empty)
.ifPresent((rate) -> rates.put(window.policy.policyName(), rate)));
Copy link

Copilot AI Dec 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The baseline() method now returns Optional, but this code unconditionally casts it to FlowCapture. While this may work in the current implementation since RetentionWindow is only used with FlowCapture instances, this cast could fail at runtime if RetentionWindow is later used with other DatapointCapture subtypes. Consider either: 1) making RetentionWindow generic over the capture type, or 2) adding a type check before the cast with appropriate error handling.

Suggested change
.map((baseline) -> calculateRate(currentCapture, (FlowCapture) baseline))
.orElseGet(OptionalDouble::empty)
.ifPresent((rate) -> rates.put(window.policy.policyName(), rate)));
.flatMap(baseline -> {
if (baseline instanceof FlowCapture) {
return calculateRate(currentCapture, (FlowCapture) baseline);
} else {
LOGGER.warn("RetentionWindow baseline is not a FlowCapture for policy '{}'; skipping rate calculation", window.policy.policyName());
return OptionalDouble.empty();
}
})
.ifPresent(rate -> rates.put(window.policy.policyName(), rate)));

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@andsel andsel requested a review from yaauie December 23, 2025 11:36
Comment on lines 100 to 106
if (b instanceof FlowCapture baseline) {
return calculateRate(currentCapture, baseline);
} else {
LOGGER.warn("Retention window `{}` provided a non-FlowCapture baseline `{}`; skipping rate calculation",
window.policy.policyName(), b);
return OptionalDouble.empty();
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I' not sure exactly what the right abstraction is, but needing to use instanceof here in the middle of this chain when FlowCapture is the only concrete implementation of DatapointCapture feels like we're setting ourselves up for leaky encapsulation.

The logic here (mapping retention windows to values) is very tightly-coupled with the nature of flow metrics, which as rate-of-change metrics relies pretty heavily on the baseline/comparison shortcuts, while I know that the proposed histogram metrics won't be using. I took a pretty big stab at generalizing this a few months ago (to make supporting your histogram work possible), but was unable at the time to find an abstraction that actually encapsulates both. While there are certainly parallels between the two, the implementation details of flow metrics clash with those of the histograms.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand your point, so I would circle back on the meaning of the retention window. The retention window is a concurrent list of datapoints, so I imagined it as container of those. The characteristic of datapoints is that are values of measures taken at a specific time, so that compaction and expiration logic (drop and remove from the list) can be applied.
If we move some aggregation logic specific to flow metric or histogram computation outside of the container then we could get a reusable data structure. For example, FlowMetric uses the baseline while the HdrHistogramFlowMetric in #18503 leverage an iteration method forEachCapture. If we move these logic outside in a class that has responsibility to compute something out of raw data, then we could reuse the code. WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With 2b8ca9a I tried to make RetentionWindow a container of subclasses of DatapointCapture. Now the ExtendedFlowMetric declare to use RetentionWindows of FlowCaptures and with this spirit in #18503 the HistogramSnapshotsWindow will declare to use RetentionWindow of HistogramCapture. In that PR the forEachCapture will remain in the RetentionWindow as legit way to iterate over the data points, unless we decide that RetentionWindow is an Iterable.

…asses od DatapointCapture.

- Added type paramters with a constrant to be a subclass of DatapointCapture which is something that have a datapoint capture time
- Peeled out baseline method into ExtendedFlowMetric, it's a wrapper on returnHead(nanos)
@andsel andsel requested a review from yaauie January 15, 2026 15:06
@elasticmachine
Copy link

💚 Build Succeeded

History

cc @andsel

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

refactor flow metrics to being able to handle also histograms and not just scalar values.

3 participants